package com.aliang.shopping.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.aliang.shopping.common.PageResult;
import com.aliang.shopping.config.OssConfig;
import com.aliang.shopping.contact.SqlColumnConstant;
import com.aliang.shopping.esDao.GoodsEsRepository;
import com.aliang.shopping.mapper.*;
import com.aliang.shopping.model.converter.CommonConverter;
import com.aliang.shopping.model.converter.GoodsConverter;
import com.aliang.shopping.model.dto.RelateDTO;
import com.aliang.shopping.model.dto.goods.GoodsEsDto;
import com.aliang.shopping.model.dto.goods.GoodsPageDto;
import com.aliang.shopping.model.po.Goods;
import com.aliang.shopping.model.po.GoodsProperties;
import com.aliang.shopping.model.po.UserCollect;
import com.aliang.shopping.model.vo.CommonUserInfoVO;
import com.aliang.shopping.model.vo.goods.GoodsVO;
import com.aliang.shopping.service.GoodsPropertiesService;
import com.aliang.shopping.service.GoodsService;
import com.aliang.shopping.util.SearchHostUtil;
import com.aliang.shopping.util.UserCFUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
 * @author ShiBaLover
 * @description 针对表【goods】的数据库操作Service实现
 * @createDate 2024-11-23 18:37:59
 */
@Service
@AllArgsConstructor
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods>
        implements GoodsService {

    private final CommonConverter<GoodsVO> commonConverter = new CommonConverter<>();
    private final GoodsPropertiesService goodsPropertiesService;
    private final GoodsConverter goodsConverter;
    private final GoodsEsRepository goodsEsRepository;
    private final SearchHostUtil searchHostUtil;
    private final OssConfig ossConfig;

    @Override
    public PageResult<GoodsVO> selectPage(GoodsPageDto pageDTO) {
        //查询数据
        IPage<Goods> page = new Page<>(pageDTO.getPageNum(), pageDTO.getPageSize());
        page = this.baseMapper.getPage(page, pageDTO);
        //转换数据
        IPage<GoodsVO> mapperPage = page.convert(goodsConverter::toVo);
        if (page.getTotal() > 0) {
            //获取全部商品Id
            List<Long> goodsId = mapperPage.getRecords().stream().map(GoodsVO::getId).distinct().collect(Collectors.toList());
            //获取商品配置
            List<GoodsProperties> list = this.goodsPropertiesService.list(
                    new LambdaQueryWrapper<GoodsProperties>()
                            .in(GoodsProperties::getGoodsId, goodsId)
            );
            //配置商品VO
            mapperPage.getRecords().forEach(goodsVO -> {
                //获取商品配置
                List<GoodsProperties> collect = list.stream()
                        .filter(goodsProperties -> goodsProperties.getGoodsId().equals(goodsVO.getId()))
                        .collect(Collectors.toList());
                //转换为VO并设置
                goodsVO.setProperties(this.goodsConverter.getPropertiesVoList(collect));
                //设置图片

                List<String> mainImages = goodsVO.getMainImages();
                //拼接主图
                if (!mainImages.isEmpty()) {
                    List<String> collect1 = mainImages.stream().map(item -> ossConfig.getUrl() + item).collect(Collectors.toList());
                    goodsVO.setMainImages(collect1);
                }
                List<String> imageDesc = goodsVO.getImageDesc();
                // 拼接图文描述
                if (!imageDesc.isEmpty()) {
                    List<String> collect1 = imageDesc.stream().map(item -> ossConfig.getUrl() + item).collect(Collectors.toList());
                    goodsVO.setImageDesc(collect1);
                }
            });
        }
        return commonConverter.toPageResult(mapperPage);
    }

    private final ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Override
    public List<GoodsEsDto> getListPageWithEs(GoodsPageDto goodsPageDto) {
        // 获取搜索词
        String keyWord = goodsPageDto.getKeyword();
        // 如果不为空，存入热搜
        if (StrUtil.isNotBlank(keyWord)) {
            Long rank = searchHostUtil.rank(keyWord);
            // 如果rank为null,则缓存里面不存在该值
            if (null == rank) {
                // 不存在,则添加,默认排序为1
                searchHostUtil.add(keyWord, 1.0);
            } else {
                // 如果存在,则获取排序值  并且+1
                int score = (int) searchHostUtil.score(keyWord);
                searchHostUtil.add(keyWord, score + 1);
            }
        }
        //构建查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //设置高亮字段
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        // 有搜索关键词才设置高亮字段
        if (StrUtil.isNotBlank(keyWord)) {
            // 查询字段
            boolQuery.should(QueryBuilders.matchQuery(SqlColumnConstant.GOODS_NAME, keyWord));
            //设置标签前缀
            highlightBuilder.preTags("<font color='red'>");
            //设置标签后缀
            highlightBuilder.postTags("</font>");
            //设置高亮字段
            highlightBuilder.field(SqlColumnConstant.GOODS_NAME);
        }
        // 构建查询
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQuery)
                .withHighlightBuilder(highlightBuilder)
                // 页码从0开始，需要减1
                .withPageable(PageRequest.of(goodsPageDto.getPageNum() - 1, goodsPageDto.getPageSize()))
                .build();
        //执行查询
        SearchHits<GoodsEsDto> searchHits = elasticsearchRestTemplate.search(searchQuery, GoodsEsDto.class);
        //获取查询结果
        List<SearchHit<GoodsEsDto>> searchHits1 = searchHits.getSearchHits();
        return searchHits1.stream().map((data) -> {
            GoodsEsDto content = data.getContent();
            // 高亮字段不为空，设置高亮字段
            if (StrUtil.isNotBlank(keyWord)) {
                List<String> goodsName = data.getHighlightField(SqlColumnConstant.GOODS_NAME);
                content.setGoodsName(goodsName.get(0));
            }
            return content;
        }).collect(Collectors.toList());
    }

    @Override
    public List<GoodsEsDto> getListWithEs(List<Long> goodsIds) {
        // 根据id获取商品数据
        Iterable<GoodsEsDto> allById = this.goodsEsRepository.findAllById(goodsIds);
        //为空，返回空数组
        if (ObjectUtil.isNull(allById))
            return Collections.emptyList();
        // 获取数据
        return StreamSupport.stream(allById.spliterator(), false).collect(Collectors.toList());
    }

    private final GoodsSkuMapper goodsSkuMapper;

    /**
     * 更新商品状态到es
     *
     * @param id
     */
    @Override
    public void updateStatusToEs(Long id) {
        //判断商品是否已存在es
        boolean exist = this.goodsEsRepository.existsById(id);
        //不存在数据，插入数据
        if (!exist) {
            this.insertGoodsToEs(this.getById(id));
        }
    }

    @Override
    public void updateGoodsToEs(Goods goods) {
        //判断商品是否已存在es
        boolean exist = this.goodsEsRepository.existsById(goods.getId());
        if (exist) {
            // 存在更新数据
            // 转换数据
            GoodsEsDto esDto = this.goodsConverter.toEsDto(goods);
            // 如果图片地址不为空，则截取图片地址
            if (StrUtil.isNotBlank(esDto.getMainImages())) {
                esDto.setMainImages(ossConfig.getUrl() + esDto.getMainImages());
            }
            //获取原始数据
            GoodsEsDto goodsEsDto = this.goodsEsRepository.findById(goods.getId()).orElse(esDto);
            // 设置价格
            esDto.setPrice(goodsEsDto.getPrice());
            // 更新数据
            this.goodsEsRepository.save(esDto);
        }
    }

    @Override
    public void deleteByEs(List<Long> ids) {
        this.goodsEsRepository.deleteAllById(ids);
    }

    @Override
    public void insertGoodsToEs(Goods goods) {
        //转换数据
        GoodsEsDto esDto = this.goodsConverter.toEsDto(goods);
        //获取最低历史价格
        BigDecimal price = this.goodsSkuMapper.selectPriceByGoodsId(goods.getId());
        // 如果图片地址不为空，则截取图片地址
        if (StrUtil.isNotBlank(esDto.getMainImages())) {
            esDto.setMainImages(ossConfig.getUrl() + esDto.getMainImages());
        }
        // 设置商品价格
        esDto.setPrice(price);
        //保存数据
        this.goodsEsRepository.save(esDto);
    }


    @Override
    public CommonUserInfoVO getBusinessInfoById(Long goodsId) {
        CommonUserInfoVO businessInfoById = this.baseMapper.getBusinessInfoById(goodsId);
        if (StrUtil.isNotBlank(businessInfoById.getAvatar())) {
            businessInfoById.setAvatar(ossConfig.getUrl() + businessInfoById.getAvatar());
        }
        return businessInfoById;
    }

    @Override
    public List<GoodsEsDto> getGoodsListById(List<Long> goodsIds) {
        Iterable<GoodsEsDto> allById = this.goodsEsRepository.findAllById(goodsIds);
        //为空，返回空数组
        if (ObjectUtil.isNull(allById))
            return Collections.emptyList();
        // 获取数据
        return StreamSupport.stream(allById.spliterator(), false).collect(Collectors.toList());
    }


    private final UserCollectMapper userCollectMapper;

    @Override
    public List<GoodsEsDto> getGoodsListByUserId(Long userId) {
        //获取收藏数据
        List<UserCollect> userCollects = this.userCollectMapper.selectList(
                new LambdaQueryWrapper<UserCollect>()
                        .select(UserCollect::getGoodsId) //查询商品Id
                        .eq(UserCollect::getUserId, userId) // 根据用户Id
                        .orderByDesc(UserCollect::getCreateTime) // 倒序
        );
        // 获取商品Id
        Set<Long> collect = userCollects.stream().map(UserCollect::getGoodsId).collect(Collectors.toSet());
        if (collect.isEmpty()) {
            return Collections.emptyList();
        }
        //根据Id查询商品数据
        Iterable<GoodsEsDto> allById = this.goodsEsRepository.findAllById(collect);
        //为空，返回空数组
        if (ObjectUtil.isNull(allById))
            return Collections.emptyList();
        // 获取数据
        return StreamSupport.stream(allById.spliterator(), false).collect(Collectors.toList());
    }

    private final UserInfoMapper userInfoMapper;
    private final CartMapper cartMapper;
    private final OrdersInfoMapper ordersInfoMapper;
    private final CommentMapper commentMapper;

    @Override
    public List<GoodsEsDto> getRecommendGoodsList(Long userId) {
        // 获取所有商品的
        List<Long> goodsIds = this.baseMapper.getIds();
        // 如果用户Id为空，返回随机的10个商品数据
        if (ObjectUtil.isNull(userId)) {
            return getRandomGoods(goodsIds);
        }

        //获取全部用户Id
        List<Long> userIds = this.userInfoMapper.selectIds();
        // 获取所有用户的收藏数据
        List<RelateDTO> userCollects = this.userCollectMapper.getRelate(userIds);
        // 获取所有用户的购物车数据
        List<RelateDTO> carts = this.cartMapper.getRelate(userIds);
        // 获取所有用户的订单数据
        List<RelateDTO> orders = this.ordersInfoMapper.getRelate(userIds);
        // 获取所以用户的评论数据
        List<RelateDTO> comments = this.commentMapper.getRelate(userIds);

        // 定义一个存储每个商品和每个用户关系的List
        List<RelateDTO> data = new ArrayList<>();
        //计算每个商品与用户的关系
        goodsIds.forEach(id -> {
            userIds.forEach(userId2 -> {
                int rate = 0;
                //收藏过商品，关系加1
                if (userCollects.stream().anyMatch(item -> Objects.equals(item.getGoodsId(), id) && Objects.equals(item.getUserId(), (userId2)))) {
                    rate += 1;
                }
                // 商品加入过购物车，关系加1
                if (carts.stream().anyMatch(item -> Objects.equals(item.getGoodsId(), id) && Objects.equals(item.getUserId(), userId2))) {
                    rate += 2;
                }
                // 购买过该商品，关系加1
                if (orders.stream().anyMatch(item -> Objects.equals(item.getGoodsId(), id) && Objects.equals(item.getUserId(), userId2))) {
                    rate += 3;
                }
                // 评论过商品车，关系加1
                if (orders.stream().anyMatch(item -> Objects.equals(item.getGoodsId(), id) && Objects.equals(item.getUserId(), userId2))) {
                    rate += 4;
                }
                // 如果关系值大于0，则添加到List中
                if (rate > 0) {
                    data.add(new RelateDTO(userId2, id, BigDecimal.valueOf(rate)));
                }
            });
        });
        // 获取推荐商品Id
        List<Long> recommendIds = UserCFUtil.recommend(userId, data);

        // 推荐Id为空，随机返回10个数据
        if (recommendIds.isEmpty()) {
            return getRandomGoods(goodsIds);
        }
        // 根据商品Id查询商品数据
        Iterable<GoodsEsDto> allById = this.goodsEsRepository.findAllById(recommendIds);
        //为空，返回空数组
        if (ObjectUtil.isNull(allById))
            return Collections.emptyList();
        //获取数据
        return StreamSupport.stream(allById.spliterator(), false).collect(Collectors.toList());
    }



    /**
     * 根据传入的商品Id随机获取10个商品数据
     *
     * @param goodsIds
     * @return
     */
    private List<GoodsEsDto> getRandomGoods(List<Long> goodsIds) {
        // 随机获取10个商品Id
        List<Long> recommendIds = new ArrayList<>(10);
        for (int i = 0; i < 10; i++) {
            int index = new Random().nextInt(goodsIds.size());
            recommendIds.add(goodsIds.get(index));
        }
        Iterable<GoodsEsDto> allById = this.goodsEsRepository.findAllById(recommendIds);
        //为空，返回空数组
        if (ObjectUtil.isNull(allById))
            return Collections.emptyList();
        //获取数据
        return StreamSupport.stream(allById.spliterator(), false).collect(Collectors.toList());
    }

    @Override
    public List<GoodsEsDto> getOrderList(String keyword) {
        return this.goodsEsRepository.findByGoodsNameLike(keyword);
    }
}




